import os
import re
import json
import luadata

def parse_lua_table(lua_str):
    # Remove the outer braces and split by commas to get key-value pairs
    table_content = lua_str.strip()[1:-1].strip()
    entries = re.split(r',\s*(?=\w)', table_content)
    
    result = {}
    for entry in entries:
        if '=' not in entry:
            continue
        
        key, value = entry.split('=', 1)
        key = key.strip().strip('"')
        value = value.strip()
        
        if value.startswith('{'):
            # Recursively parse nested tables
            value = parse_lua_table(value)
        elif value.startswith('"'):
            # Strip quotes from strings
            value = value.strip('"')
        
        result[key] = value
    
    return result

class XmakeRepo:

    def __init__(self):
        self.packages_dir = 'packages'
        self.repo_packages_dir  = 'repositories/xmake-repo/packages'
        self.repo_artifacts_dir = 'repositories/build-artifacts/packages'

        self.packages_map = {}
        self.packages_info_map = {}
        
        self.repo_packages_map = {}
        self.repo_packages_info_map = {}


        self.repo_artifacts_map = {}
        self.repo_artifacts_info_map = {}

    def load(self, dir):
        self.load_packages(os.path.join(dir, self.packages_dir))
        self.load_repo_packages(os.path.join(dir, self.repo_packages_dir))
        self.load_repo_artifacts(os.path.join(dir, self.repo_artifacts_dir))

    # packages

    def load_packages(self, dir):
        '''.../packages/c'''
        for name in os.listdir(dir):
            path = os.path.join(dir, name)
            if os.path.isdir(path):
                self.load_packages_info(path)

    def load_packages_info(self, dir):
        '''.../packages/c/cjson'''
        print('load package: %s' % dir)
        for pkg in os.listdir(dir):
            path = os.path.join(dir, pkg)
            if os.path.isdir(path):
                self.packages_map[pkg] = self.load_package_versions(path)

    def load_package_versions(self, dir):
        '''.../packages/c/cjson/v1.7.18'''
        print('load package versions: %s' % dir)
        pkg_versions = {}
        for verdir in os.listdir(dir):
            path = os.path.join(dir, verdir)
            if os.path.isdir(path):
                pkg_versions[verdir] = self.load_package_version_maybe_uuid(path)
        return pkg_versions
    

    def load_package_version_maybe_uuid(self, dir):
        '''.../packages/c/cjson/v1.7.18/<uuid>'''
        for uuiddir in os.listdir(dir):
            path = os.path.join(dir, uuiddir)
            if os.path.isdir(path):
                return self.load_package_version(path)
    
    def load_package_version(self, dir):
        '''.../packages/c/cjson/v1.7.18/<uuid>/manifest.txt'''
        manifest_path = os.path.join(dir, 'manifest.txt')
        if os.path.isfile(manifest_path):
            # manifest = open(manifest_path, 'r').read()
            # return parse_lua_table(manifest)
            return luadata.read(manifest_path, encoding="utf-8")
        return {}


    # repo packages
    def load_repo_packages(self, dir):
        '''.../repositories/xmake-repo/packages/l'''
        for name in os.listdir(dir):
            path = os.path.join(dir, name)
            if os.path.isdir(path):
                self.load_repo_packages_info(path)

    def load_repo_packages_info(self, dir):
        '''.../repositories/xmake-repo/packages/l/libpng'''
        # print('load repo package: %s' % dir)
        for pkg in os.listdir(dir):
            path = os.path.join(dir, pkg)
            if os.path.isdir(path):
                self.repo_packages_map[pkg] = self.load_repo_package_xmake(path)
                # self.load_repo_package_info(path)

    # def load_repo_package_info(self, dir):
    #     '''.../repositories/xmake-repo/packages/l/libpng/v1.6.40'''
    #     print('load repo package info: %s' % dir)
    #     for pkg in os.listdir(dir):
    #         path = os.path.join(dir, pkg)
    #         if os.path.isdir(path):
    #             self.repo_packages_map[pkg] = self.load_repo_package_xmake(path)

    def load_repo_package_xmake(self, dir):
        '''.../repositories/xmake-repo/packages/l/libpng/xmake.lua'''
        xmake_path = os.path.join(dir, 'xmake.lua')
        print('xmake: %s' % xmake_path)
        if os.path.isfile(xmake_path):
            return open(xmake_path, 'r').read()
        return ""

    # repo artifacts
    def load_repo_artifacts(self, dir):
        '''../repositories/build-artifacts/l'''
        # print('load repo artifacts: %s' % dir)
        for name in os.listdir(dir):
            path = os.path.join(dir, name)
            if os.path.isdir(path):
                self.load_repo_artifact_info(path)

    def load_repo_artifact_info(self, dir):
        '''.../l/libpng'''
        # print('load repo artifact: %s' % dir)
        for artifact in os.listdir(dir):
            path = os.path.join(dir, artifact)
            if os.path.isdir(path):
                self.repo_artifacts_map[artifact] = self.load_repo_artifact_versions(path)
    def load_repo_artifact_versions(self, dir):
        '''.../l/libpng/v1.6.40'''
        # print('load repo artifact versions: %s' % dir)
        artifact_versions = {}
        kdri = os.path.basename(dir)
        
        for verdir in os.listdir(dir):
            path = os.path.join(dir, verdir)
            if os.path.isdir(path):
                artifact_versions[verdir] = self.load_repo_artifact_version(path)
        return artifact_versions

    def load_repo_artifact_version(self, dir):
        '''.../l/libpng/v1.6.40/manifest.txt'''
        # print('load artifact version: %s' % dir)
        
        manifest_path = os.path.join(dir, 'manifest.txt')
        # print('manifest: %s' % manifest_path)
        if os.path.isfile(manifest_path):
            manifest = open(manifest_path, 'r').read()
            # return parse_lua_table(manifest)
            return luadata.read(manifest_path, encoding="utf-8")
        return {}


class XmakeRepoAnalyzer:
    
    def __init__(self):
        self.repo = XmakeRepo()
        self.repo.load(os.path.join(os.getenv('HOME'), '.xmake'))
    # installed pkgs
    def pkgs(self):
        return self.repo.packages_map.keys()
    
    def get_pkg(self, pkg, version = None):
        if pkg in self.repo.packages_map:
            if version is not None:
                if version in self.repo.packages_map[pkg]:
                    return self.repo.packages_map[pkg][version]
            return self.repo.packages_map[pkg]
        return None
    
    def get_pkg_versions(self, pkg):
        if pkg in self.repo.packages_map:
            return self.repo.packages_map[pkg].keys()
        return []
    
    # xrepo pkgs 
    def repo_pkgs(self):
        return self.repo.repo_packages_map.keys()
    
    def get_repo_pkg(self, pkg):
        if pkg in self.repo.repo_packages_map:
            return self.repo.repo_packages_map[pkg]
        return ""
    
    # xrepo build artifacts
    def repo_artifacts(self):
        return self.repo.repo_artifacts_map.keys()
    
    def get_artifact(self, pkg, version = None):
        if pkg in self.repo.repo_artifacts_map:
            if version is not None:
                if version in self.repo.repo_artifacts_map[pkg]:
                    return self.repo.repo_artifacts_map[pkg][version]
            return self.repo.repo_artifacts_map[pkg]
        return None
    
    def get_artifact_versions(self, pkg):
        if pkg in self.repo.repo_artifacts_map:
            return self.repo.repo_artifacts_map[pkg].keys()
        return []
    

    # helper functions
    def help_get_pkg_some(self, name, some):
        if name in self.repo.repo_packages_map:
            pkg = self.get_repo_pkg(name)
            pos = pkg.find(some)
            if pos == -1:
                return ''
            start = pkg.find('"', pos) + 1
            end = pkg.find('"', start)
            return pkg[start:end]
        return ''

    # def help_get_pkg_somes(self, name, some):
    #     somes = []
    #     if name in self.repo.repo_packages_map:
    #         pkg = self.get_repo_pkg(name)
    #         for line in pkg.splitlines():
    #             if line.find(some) != -1:
    #                 somes.append(line)
    #     return somes
    
    def help_get_pkg_some_reg_findall(self, name, reg):
        values = []
        if name in self.repo.repo_packages_map:
            pkg = self.get_repo_pkg(name)
            import re
            return re.findall(reg, pkg)
        return values

    def help_get_pkg_homepage(self, name):
        return self.help_get_pkg_some(name, 'set_homepage')

    def help_get_pkg_add_urls(self, name):
        '''
        e.g:
            add_urls("https://github.com/libuv/libuv/archive/refs/tags/v1.44.2.tar.gz")
        '''
        urls = []
        if name in self.repo.repo_packages_map:
            pkg = self.get_repo_pkg(name)
            if 'add_urls' in pkg:
                pkg.find('add_urls')
                start = pkg.find('add_urls("')
                end = pkg.find('")', start)
                urls = pkg[start+10:end].replace('"', '').replace('\n', '').replace(' ', '').split(',')
        return urls
    
    def help_get_pkg_license(self, name):
        return self.help_get_pkg_some(name, 'set_license')
    def help_get_pkg_description(self, name):
        return self.help_get_pkg_some(name, 'set_description')
    def help_get_pkg_versions(self, name):
        versions = self.help_get_pkg_some_reg_findall(name, r'add_versions\("([^"]+)')
        return ', '.join(versions)

if __name__ == '__main__':
    xmake_dir = '/home/zinface/.xmake'
    
    # repo = XmakeRepo()
    # repo.load(xmake_dir)

    # libpng = repo.repo_artifacts_map['libpng']
    # libpng_versions = [ver for ver in libpng]
    # print(libpng)


    xma = XmakeRepoAnalyzer()
    print('packages:', len(xma.pkgs()))
    print('packages:', len(xma.pkgs()))
    # print('artifacts:', len(xma.repo_pkgs()))
    
    # print('libpng versions:', xma.get_artifact_versions('libpng'))
    # print('libpng v1.6.40:\n', json.dumps(xma.get_artifact('libpng', 'v1.6.40'), indent=2))